package com.swan.knife4j.core;

import cn.hutool.core.comparator.CompareUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.core.providers.SpringDocProviders;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;
import java.util.stream.Collectors;

/** 分组API中，Api顺序按 @Tag 标签中的name属性进行排序 */
@Slf4j
@Aspect
public class ApiSortAspect implements InitializingBean {

    @Autowired
    private SpringDocProviders springDocProviders;

    @Autowired
    private SpringDocConfigProperties springDocConfigProperties;

    private ObjectMapper objectMapper;

    // 获取分组下的接口详情列表接口: 分组和全部同时切
    @Pointcut("execution(byte[] org.springdoc.webmvc.api.OpenApiResource.openapiJson(..)) " +
            "|| execution(byte[] org.springdoc.webmvc.api.MultipleOpenApiWebMvcResource.openapiJson(..))")
    public void pointCut() {}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        // 执行方法
        Object response = null;
        try {
            response =  joinPoint.proceed();

            // 反向序列化为 openApi 对象
            OpenAPI openAPI = this.objectMapper.readerFor(OpenAPI.class)
                    .createParser((byte[]) response)
                    .readValueAs(OpenAPI.class);

            // 根据Tag进行排序
            if (Objects.nonNull(openAPI.getTags())) {

                openAPI.getTags().sort(Comparator.comparing(Tag::getName));

                //重新序列化为 json 对象
                response = toJsonString(openAPI);
            }

            // Controller 中按 summary 排序
            if (Objects.nonNull(openAPI.getPaths())) {

                List<Map.Entry<String, PathItem>> sortedPathItem = openAPI.getPaths().entrySet().stream().sorted((a, b) -> {

                    Operation aOperation = Objects.nonNull( a.getValue().getPost()) ? a.getValue().getPost() :
                            Objects.nonNull(a.getValue().getGet()) ? a.getValue().getGet() :
                                    Objects.nonNull(a.getValue().getPut()) ? a.getValue().getPut() :
                                            Objects.nonNull(a.getValue().getDelete()) ? a.getValue().getDelete() :
                                                    Objects.nonNull(a.getValue().getPatch()) ? a.getValue().getPatch() : null;

                    Operation bOperation = Objects.nonNull( b.getValue().getPost()) ? b.getValue().getPost() :
                            Objects.nonNull(b.getValue().getGet()) ? b.getValue().getGet() :
                                    Objects.nonNull(b.getValue().getPut()) ? b.getValue().getPut() :
                                            Objects.nonNull(b.getValue().getDelete()) ? b.getValue().getDelete() :
                                                    Objects.nonNull(b.getValue().getPatch()) ? b.getValue().getPatch() : null;

                    if (aOperation == null ) {
                        return -1;
                    }
                    if (bOperation == null) {
                        return -1;
                    }
                    int compare = CompareUtil.compare(aOperation.getTags().get(0), bOperation.getTags().get(0));
                    if (compare == 0) {
                        compare =CompareUtil.compare(aOperation.getSummary(), bOperation.getSummary());
                    }
                    return compare;
                }).collect(Collectors.toList());

                Paths newPaths = new Paths();
                for (Map.Entry<String, PathItem> entry : sortedPathItem) {
                    newPaths.addPathItem(entry.getKey(), entry.getValue());
                }
                openAPI.setPaths(newPaths);
            }
            response = toJsonString(openAPI).getBytes();

        } catch (Throwable tx){
            log.warn("解析错误", tx);
        }

        // 如果序列化失败了，则返回原始对象
        return response;
    }

    protected String toJsonString(OpenAPI openAPI) throws JsonProcessingException {
        String result = null;
        if (!springDocConfigProperties.isWriterWithDefaultPrettyPrinter()) {
            result = objectMapper.writerFor(OpenAPI.class).writeValueAsString(openAPI);
        } else {
            result = objectMapper.writerWithDefaultPrettyPrinter().forType(OpenAPI.class).writeValueAsString(openAPI);
        }
        return result;
    }


    @Override
    public void afterPropertiesSet() throws Exception {

        this.objectMapper = springDocProviders.jsonMapper();

        if (springDocConfigProperties.isWriterWithOrderByKeys()) {
            ObjectMapperProvider.sortOutput(objectMapper, springDocConfigProperties);
        }

    }
}
